
#define	WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>

#include <cstdio>
#include <cstdlib>
#include <malloc.h>
#include <cassert>

#include "allocator.h"

//#define	LARGE_MEMORY


//#define	CRT_LFH
//#define	TBBMALLOC
//#define	TCMALLOC
//#define	MS_CONCURRENCY
#define	LOOKASIDE_HEAP

#if	defined( MS_CONCURRENCY )
#if	_MSC_VER < 1600
#undef	MS_CONCURRENCY
#define	CRT_LFH
#endif
#endif

#if defined( TBBMALLOC )
    #pragma comment( lib, "./lib/tbbmalloc_proxy.lib" )
    #pragma comment( linker, "/include:__TBB_malloc_proxy" )
#elif defined( TCMALLOC )
	#pragma comment( lib, "./lib/libtcmalloc_minimal.lib" )
    #pragma comment( linker, "/include:__tcmalloc" )
#endif

#ifdef	MS_CONCURRENCY
#include <ppl.h>
#endif


const int thread_count = 9;
#ifdef	LARGE_MEMORY
const int run_count = 1000;
#else
const int run_count = 10000;
#endif
const int loop_count = 200;


#ifdef	LOOKASIDE_HEAP
memory::allocator allocator;
#endif


void	set_lfh()
{
	HANDLE heaps[64] = { nullptr, };
	DWORD count = ::GetProcessHeaps( sizeof( heaps )/sizeof( heaps[0] ), heaps );

	::wprintf( L"process heap count: %u\n", count );
	for( DWORD i = 0; i < count; ++i )
	{
		ULONG lfh = 2;
		if( ::HeapSetInformation( heaps[i], HeapCompatibilityInformation, &lfh, sizeof( lfh ) ) == FALSE )
		{
			::wprintf( L"error process heap lfh.\n" );
		}
	}
}

void	print_allocator_stat( const memory::allocator* allocator )
{
	const memory::statistics* stat = allocator->get_statistics();
	assert( stat != nullptr );

	size_t heap_alloc = 0;
	size_t heap_free = 0;
	for( size_t i = 0; i < stat->get_heap_count(); ++i )
	{
		const memory::heap_statistics_t* heap = stat->get_heap( i );
		assert( heap != nullptr );
		if( heap != nullptr )
		{
			::wprintf( L"heap[%u]: alloc: %u, free: %u\n", i, heap->alloc, heap->free );

			heap_alloc += heap->alloc;
			heap_free += heap->free;
		}
	}

	size_t lookaside_alloc = 0;
	size_t lookaside_free = 0;
	for( size_t i = 0; i < stat->get_cache_count(); ++i )
	{
		const memory::cache_statistics_t* cache = stat->get_cache( i );
		assert( cache != nullptr );
		if( cache != nullptr )
		{
			size_t cache_alloc = 0;
			size_t cache_free = 0;
			for( size_t j = 0; j < cache->count; ++j )
			{
				const memory::lookaside_statistics_t* lookaside = &cache->lookaside[j];
				cache_alloc += lookaside->alloc;
				cache_free += lookaside->free;
			}

			lookaside_alloc += cache_alloc;
			lookaside_free += cache_free;

			::wprintf( L"cache[%u]: alloc: %u, free: %u\n", cache_alloc, cache_free );
		}
	}

	::wprintf( L"\nheap(%u) alloc: %u, free: %u\n", stat->get_heap_count(), heap_alloc, heap_free );
	::wprintf( L"lookaside(%u) alloc: %u, free: %u\n", stat->get_cache_count(), lookaside_alloc, lookaside_free );
	::wprintf( L"total alloc: %u, total free: %u\n", heap_alloc + lookaside_alloc, heap_free + lookaside_free );
}

static unsigned int	__stdcall	memory_test_thread_func( void* )
{
	int count = 0;
	char* temp[loop_count] = { nullptr, };

	while( count < run_count )
	{
		for( int i = 0; i < loop_count; ++i )
		{
			int size = ::rand() % (8*1024);
#ifdef	LARGE_MEMORY
			size += 16*1024;
#endif

#if		defined( CRT_LFH ) || defined( TBBMALLOC ) || defined( TCMALLOC )
			temp[i] = new char[size];
#elif	defined( MS_CONCURRENCY )
			temp[i] = reinterpret_cast< char* >( Concurrency::Alloc( size ) );
#elif	defined( LOOKASIDE_HEAP )
			temp[i] = reinterpret_cast< char* >( allocator.alloc( size ) );
#endif

			//::memset( temp[i], 0xff, size );
		}

		for( int i = 0; i < loop_count; ++i )
		{
#if		defined( CRT_LFH ) || defined( TBBMALLOC ) || defined( TCMALLOC )
			delete [] temp[i];
#elif	defined( MS_CONCURRENCY )
			Concurrency::Free( temp[i] );
#elif	defined( LOOKASIDE_HEAP )
			allocator.free( temp[i] );
#endif

			temp[i] = nullptr;
		}

		++count;
	}

	return 0;
}

int	main()
{
#if		defined( CRT_LFH )

	::wprintf( L"crt lfh\n" );

	set_lfh();

#elif	defined( TBBMALLOC )

	::wprintf( L"tbbmalloc\n" );

#elif	defined( TCMALLOC )

	::wprintf( L"tcmalloc\n" );

#elif	defined( MS_CONCURRENCY )

	::wprintf( L"ms concurrency\n" );

#elif	defined( LOOKASIDE_HEAP )

	::wprintf( L"lookaside heap\n" );

	if( allocator.create() == false )
	{
		::wprintf( L"allocator.create() failed.\n" );
		return 0;
	}

#endif

	HANDLE thread_handle[thread_count] = { nullptr, };

	DWORD start = ::GetTickCount();
	for( int i = 0; i < thread_count; ++i )
	{
		unsigned int id = 0;
		thread_handle[i] = reinterpret_cast< HANDLE >( ::_beginthreadex( nullptr, 0, memory_test_thread_func, nullptr, 0, &id ) );
		if( thread_handle[i] == nullptr )
		{
			wprintf( L"%d: _beginthreadex failed.\n", i );
		}
	}

	::WaitForMultipleObjects( thread_count, thread_handle, TRUE, INFINITE );

	DWORD run_time = ::GetTickCount() - start;
	::wprintf( L"run time: %d\n", run_time );

	for( int i = 0; i < thread_count; ++i )
	{
		if( thread_handle[i] != nullptr )
		{
			::CloseHandle( thread_handle[i] );
			thread_handle[i] = nullptr;
		}
	}

#ifdef	LOOKASIDE_HEAP
	print_allocator_stat( &allocator );
	allocator.destroy();
#endif

	return 0;
}
